/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
public final class Streams {
    private Streams() {}

    static final int BUFFER_SIZE = 8192;

    public static void transfer(InputStream is, OutputStream os) throws IOException {
        transfer(is, os, null);
    }

    public static void transfer(InputStream is, OutputStream os, TransferCallback callback) throws IOException {
        byte[] buf = new byte[BUFFER_SIZE];
        int len;
        if (callback == null) {
            while ((len = is.read(buf)) > 0) {
                os.write(buf, 0, len);
            }
        } else {
            while ((len = is.read(buf)) > 0) {
                os.write(buf, 0, len);
                callback.completed(buf,0, len);
            }
        }
    }

    public interface TransferCallback {
        void completed(byte[] buf, int off, int len);
    }

    /**
     * 将文件输出到输出流中
     * @param sourceFile
     * @param targetOs
     * @param snapshoot 如果文件正在做追加操作，为 true 时只输出操作时的快照内容
     * @throws Exception
     */
    public static void transfer(File sourceFile, OutputStream targetOs, boolean snapshoot) throws Exception {
        byte[] buf = new byte[10240];
        long fileLen = sourceFile.length();
        long outLen = 0;
        try (FileInputStream fis = new FileInputStream(sourceFile)) {
            int len;
            while ((len = fis.read(buf)) != -1) {
                targetOs.write(buf, 0, len);
                if (snapshoot) {
                    outLen += len;
                    if (outLen >= fileLen) {
                        break;
                    }
                }
            }
        }
    }

    public static byte[] toByteArray(InputStream is) throws IOException {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[10240];
        int len;
        while ((len = is.read(buff)) > 0) {
            swapStream.write(buff, 0, len);
        }
        return swapStream.toByteArray();
    }

    public static String toString(InputStream is, String charsetName) throws IOException {
        return new String(toByteArray(is), charsetName);
    }

    public static Md5OutputStream md5OutputStream(OutputStream os) throws NoSuchAlgorithmException {
        return new Md5OutputStream(os);
    }

    public static class Md5OutputStream extends OutputStream {

        private final OutputStream os;
        private final MessageDigest digest;

        private Md5OutputStream(OutputStream os) throws NoSuchAlgorithmException {
            this.os = os;
            this.digest = MessageDigest.getInstance("MD5");
        }

        public byte[] md5() {
            return digest.digest();
        }

        @Override
        public void write(int b) throws IOException {
            digest.update((byte)b);
            os.write(b);
        }

        @Override
        public void write(byte[] bytes) throws IOException {
            digest.update(bytes);
            os.write(bytes);
        }

        @Override
        public void write(byte[] bytes, int off, int len) throws IOException {
            digest.update(bytes, off, len);
            os.write(bytes, off, len);
        }

        @Override
        public void flush() throws IOException {
            os.flush();
        }

        @Override
        public void close() throws IOException {
            os.close();
        }
    }
}
